package com.example.blog.modules.article.service.impl;

import com.example.blog.common.exception.ServiceException;
import com.example.blog.common.security.SecurityService;
import com.example.blog.modules.article.dto.ArticleDTO;
import com.example.blog.modules.article.dto.ArticleQuery;
import com.example.blog.modules.article.dto.ArticleRequest;
import com.example.blog.modules.article.entity.Article;
import com.example.blog.modules.article.entity.Category;
import com.example.blog.modules.article.entity.Tag;
import com.example.blog.modules.article.repository.ArticleRepository;
import com.example.blog.modules.article.repository.CategoryRepository;
import com.example.blog.modules.article.repository.TagRepository;
import com.example.blog.modules.article.service.ArticleService;
import com.example.blog.modules.user.entity.User;
import com.example.blog.modules.user.repository.UserRepository;
import jakarta.persistence.criteria.Predicate;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
@RequiredArgsConstructor
public class ArticleServiceImpl implements ArticleService {

    private final ArticleRepository articleRepository;
    private final CategoryRepository categoryRepository;
    private final TagRepository tagRepository;
    private final UserRepository userRepository;
    private final SecurityService securityService;

    @Override
    @Transactional
    public ArticleDTO createArticle(ArticleRequest request) {
        Article article = new Article();
        updateArticleFromRequest(article, request);
        
        // 设置作者
        User currentUser = userRepository.findByUsername(securityService.getCurrentUsername())
                .orElseThrow(() -> new ServiceException("用户不存在"));
        article.setAuthor(currentUser);
        
        return convertToDTO(articleRepository.save(article));
    }

    @Override
    @Transactional
    public ArticleDTO updateArticle(Long id, ArticleRequest request) {
        Article article = articleRepository.findById(id)
                .orElseThrow(() -> new ServiceException("文章不存在"));
        
        // 检查权限
        if (!securityService.isCurrentUser(article.getAuthor().getId()) && 
            !securityService.hasRole("ROLE_ADMIN")) {
            throw new ServiceException("没有权限修改此文章");
        }
        
        updateArticleFromRequest(article, request);
        return convertToDTO(articleRepository.save(article));
    }

    @Override
    @Transactional
    public void deleteArticle(Long id) {
        Article article = articleRepository.findById(id)
                .orElseThrow(() -> new ServiceException("文章不存在"));
        
        // 检查权限
        if (!securityService.isCurrentUser(article.getAuthor().getId()) && 
            !securityService.hasRole("ROLE_ADMIN")) {
            throw new ServiceException("没有权限删除此文章");
        }
        
        articleRepository.delete(article);
    }

    @Override
    @Transactional(readOnly = true)
    public ArticleDTO getArticle(Long id) {
        return convertToDTO(articleRepository.findById(id)
                .orElseThrow(() -> new ServiceException("文章不存在")));
    }

    @Override
    @Transactional(readOnly = true)
    public Page<ArticleDTO> getArticles(ArticleQuery query, Pageable pageable) {
        Specification<Article> spec = (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            
            if (StringUtils.hasText(query.getKeyword())) {
                predicates.add(criteriaBuilder.or(
                    criteriaBuilder.like(root.get("title"), "%" + query.getKeyword() + "%"),
                    criteriaBuilder.like(root.get("content"), "%" + query.getKeyword() + "%"),
                    criteriaBuilder.like(root.get("summary"), "%" + query.getKeyword() + "%")
                ));
            }
            
            if (query.getCategoryId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("category").get("id"), query.getCategoryId()));
            }
            
            if (query.getTagIds() != null && !query.getTagIds().isEmpty()) {
                predicates.add(root.join("tags").get("id").in(query.getTagIds()));
            }
            
            if (query.getIsPublished() != null) {
                predicates.add(criteriaBuilder.equal(root.get("isPublished"), query.getIsPublished()));
            }
            
            if (query.getIsTop() != null) {
                predicates.add(criteriaBuilder.equal(root.get("isTop"), query.getIsTop()));
            }
            
            if (query.getAuthorId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("author").get("id"), query.getAuthorId()));
            }
            
            if (query.getStartTime() != null) {
                predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), query.getStartTime()));
            }
            
            if (query.getEndTime() != null) {
                predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("createTime"), query.getEndTime()));
            }
            
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
        
        return articleRepository.findAll(spec, pageable).map(this::convertToDTO);
    }

    @Override
    @Transactional
    public void incrementViewCount(Long id) {
        articleRepository.incrementViewCount(id);
    }

    @Override
    @Transactional
    public void incrementLikeCount(Long id) {
        articleRepository.incrementLikeCount(id);
    }

    @Override
    @Transactional
    public void decrementLikeCount(Long id) {
        articleRepository.decrementLikeCount(id);
    }

    @Override
    @Transactional
    public void publishArticle(Long id) {
        Article article = articleRepository.findById(id)
                .orElseThrow(() -> new ServiceException("文章不存在"));
        article.setIsPublished(true);
        articleRepository.save(article);
    }

    @Override
    @Transactional
    public void unpublishArticle(Long id) {
        Article article = articleRepository.findById(id)
                .orElseThrow(() -> new ServiceException("文章不存在"));
        article.setIsPublished(false);
        articleRepository.save(article);
    }

    @Override
    @Transactional
    public void topArticle(Long id) {
        Article article = articleRepository.findById(id)
                .orElseThrow(() -> new ServiceException("文章不存在"));
        article.setIsTop(true);
        articleRepository.save(article);
    }

    @Override
    @Transactional
    public void untopArticle(Long id) {
        Article article = articleRepository.findById(id)
                .orElseThrow(() -> new ServiceException("文章不存在"));
        article.setIsTop(false);
        articleRepository.save(article);
    }

    @Override
    @Transactional
    public void enableComment(Long id) {
        Article article = articleRepository.findById(id)
                .orElseThrow(() -> new ServiceException("文章不存在"));
        article.setAllowComment(true);
        articleRepository.save(article);
    }

    @Override
    @Transactional
    public void disableComment(Long id) {
        Article article = articleRepository.findById(id)
                .orElseThrow(() -> new ServiceException("文章不存在"));
        article.setAllowComment(false);
        articleRepository.save(article);
    }

    private void updateArticleFromRequest(Article article, ArticleRequest request) {
        article.setTitle(request.getTitle());
        article.setContent(request.getContent());
        article.setSummary(request.getSummary());
        article.setCoverImage(request.getCoverImage());
        article.setIsPublished(request.getIsPublished());
        article.setIsTop(request.getIsTop());
        article.setAllowComment(request.getAllowComment());
        
        // 设置分类
        if (request.getCategoryId() != null) {
            Category category = categoryRepository.findById(request.getCategoryId())
                    .orElseThrow(() -> new ServiceException("分类不存在"));
            article.setCategory(category);
        }
        
        // 设置标签
        if (request.getTagIds() != null && !request.getTagIds().isEmpty()) {
            Set<Tag> tags = new HashSet<>(tagRepository.findByIdIn(request.getTagIds()));
            if (tags.size() != request.getTagIds().size()) {
                throw new ServiceException("部分标签不存在");
            }
            article.setTags(tags);
        }
    }

    private ArticleDTO convertToDTO(Article article) {
        ArticleDTO dto = new ArticleDTO();
        dto.setId(article.getId());
        dto.setTitle(article.getTitle());
        dto.setContent(article.getContent());
        dto.setSummary(article.getSummary());
        dto.setCoverImage(article.getCoverImage());
        dto.setViewCount(article.getViewCount());
        dto.setLikeCount(article.getLikeCount());
        dto.setCommentCount(article.getCommentCount());
        dto.setIsPublished(article.getIsPublished());
        dto.setIsTop(article.getIsTop());
        dto.setAllowComment(article.getAllowComment());
        dto.setCreateTime(article.getCreateTime());
        dto.setUpdateTime(article.getUpdateTime());
        
        // 设置作者信息
        if (article.getAuthor() != null) {
            dto.setAuthor(convertToUserDTO(article.getAuthor()));
        }
        
        // 设置分类信息
        if (article.getCategory() != null) {
            dto.setCategory(convertToCategoryDTO(article.getCategory()));
        }
        
        // 设置标签信息
        if (article.getTags() != null) {
            dto.setTags(article.getTags().stream()
                    .map(this::convertToTagDTO)
                    .collect(java.util.stream.Collectors.toSet()));
        }
        
        return dto;
    }

    private com.example.blog.modules.user.dto.UserDTO convertToUserDTO(User user) {
        com.example.blog.modules.user.dto.UserDTO dto = new com.example.blog.modules.user.dto.UserDTO();
        dto.setId(user.getId());
        dto.setUsername(user.getUsername());
        dto.setNickname(user.getNickname());
        dto.setAvatar(user.getAvatar());
        return dto;
    }

    private com.example.blog.modules.article.dto.CategoryDTO convertToCategoryDTO(Category category) {
        com.example.blog.modules.article.dto.CategoryDTO dto = new com.example.blog.modules.article.dto.CategoryDTO();
        dto.setId(category.getId());
        dto.setName(category.getName());
        dto.setDescription(category.getDescription());
        dto.setSort(category.getSort());
        dto.setCreateTime(category.getCreateTime());
        dto.setUpdateTime(category.getUpdateTime());
        return dto;
    }

    private com.example.blog.modules.article.dto.TagDTO convertToTagDTO(Tag tag) {
        com.example.blog.modules.article.dto.TagDTO dto = new com.example.blog.modules.article.dto.TagDTO();
        dto.setId(tag.getId());
        dto.setName(tag.getName());
        dto.setDescription(tag.getDescription());
        dto.setSort(tag.getSort());
        dto.setCreateTime(tag.getCreateTime());
        dto.setUpdateTime(tag.getUpdateTime());
        return dto;
    }
} 